/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
* IOUtil.java
*
* Created on November 17, 2007, 10:10 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
 */
package javautil.io;

import javautil.collections.ArrayUtil;
import javautil.lang.StringUtil;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.text.Format;
import java.util.StringTokenizer;

/**
 *
 * @author stewari1
 */
public class IOUtil {

    /** Creates a new instance of IOUtil */
    public IOUtil() {}

    public static void main(String[] args) {
        recursiveDelete(new File("C:/Documents and Settings/stewari1/Desktop/GenomeMap"), ".svn");
    }

    public static byte[] getBytesFromFile(File file) throws IOException {

        byte[] bytes;

        try (InputStream is = new FileInputStream(file)) {

            long length = file.length();

            if (length > Integer.MAX_VALUE) {


                // File is too large
            }

            bytes = new byte[(int) length];

            int offset  = 0;
            int numRead = 0;

            while ((offset < bytes.length)
                    && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {

                offset += numRead;
            }

            if (offset < bytes.length) {
                throw new IOException("Could not completely read file " + file.getName());
            }
        }

        return bytes;
    }

    /**
     * Calculates row size(sample size) of the data file. header is excluded from
     * calculation if present.
     */
    public static int countObs(File file, boolean headerPresent)
            throws FileNotFoundException, IOException {

        int result    = 0;
        String line   = "";
        FileReader fr = new FileReader(file);

        try (BufferedReader inFile = new BufferedReader(fr)) {

            if (headerPresent) {
                line = inFile.readLine();    /* This one ignores the header from counting */
            }

            line = inFile.readLine();

            while (line != null) {

                result++;

                line = inFile.readLine();
            }
        }

        return result;
    }

    /**
     * if <CODE>delim</CODE> is <CODE>null</CODE>; use the default.
     * @param line
     * @param delim
     * @return
     * @deprecated
     */
    public static String[] getTokens(String line, String delim) {

        StringTokenizer tk = null;

        if (delim == null) {
            tk = new StringTokenizer(line);
        } else {
            tk = new StringTokenizer(line, delim);
        }

        String[] data = new String[tk.countTokens()];

        for (int index = 0; index < data.length; index++) {

            data[index] = tk.nextToken();
        }

        return data;
    }

    public static Object unMarshall(File file)
            throws FileNotFoundException, IOException, ClassNotFoundException {

        FileInputStream fis = new FileInputStream(file);
        Object obj;

        try (ObjectInputStream ois = new ObjectInputStream(fis)) {
            obj = ois.readObject();
        }

        return obj;
    }

    public static void marshall(File file, Object obj) throws FileNotFoundException, IOException {

        FileOutputStream fos = new FileOutputStream(file);

        try (ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(obj);
        }
    }

    /**
     * Read the entire document in a string
     * @param file
     * @return
     * @throws java.io.FileNotFoundException
     * @throws java.io.IOException
     */
    public static String readFileContentToString(File file)
            throws FileNotFoundException, IOException {

        String docString          = "";
        BufferedReader buffReader = new BufferedReader(new FileReader(file));

        for (int val = buffReader.read(); val != -1; ) {

            docString += (char) val;
            val       = buffReader.read();
        }

        return docString;
    }

    public static void writeToFile(File file, String str) throws IOException {

        try (PrintWriter outFile = new PrintWriter(new BufferedWriter(new FileWriter(file,
                false)))) {
            outFile.print(str);
        }
    }

    public static void writeToFile(File file, byte[] bytes) throws IOException {

        try (FileOutputStream os = new FileOutputStream(file)) {
            os.write(bytes);
        }
    }

    public static <T> String toString(T[] array, String delim) {

        String str = "";

        for (int i = 0; i < array.length - 1; i++) {

            str += array[i] + delim;
        }

        return str + array[array.length - 1];
    }

    /**
     * the array provided must be an object for the formatter to work.
     * <code>formatter.format(Object)</code> is used to do the formatting.
     * @param array
     * @param delim
     * @param formatter
     * @return
     */
    public static <T> String toString(T[] array, String delim, Format formatter) {

        String str = "";

        for (int i = 0; i < array.length - 1; i++) {

            str += formatter.format(array[i]) + delim;
        }

        return str + formatter.format(array[array.length - 1]);
    }

    public static String toString(Object[][] array, String colDelim, String rowDelim) {

        String str = "";

        for (Object[] row : array) {

            str += toString(row, colDelim) + rowDelim;
        }

        return str;
    }

    public static String toString(Object[][] objs2D, int horiCellPadding, int vertCellPadding,
                                  Format format) {

        StringBuilder strBuilder = new StringBuilder();
        int maxStrLen = (format == null)
                        ? getMaxStringLen(objs2D) : getMaxStringLen(objs2D, format);

        for (int index = 0; index < objs2D.length - 1; index++) {

            strBuilder.append(printRow(objs2D[index], horiCellPadding, maxStrLen, format));

            for (int i = 0; i < vertCellPadding; i++) {

                strBuilder.append("\n");
            }
        }

        strBuilder.append(printRow(objs2D[objs2D.length - 1], horiCellPadding, maxStrLen, format));
        strBuilder.append("\n");

        return strBuilder.toString();
    }

    /**
     * Method description
     *
     * @param objs description
     * @param horiCellPadding description
     * @param maxStringLen description
     * @param format description
     *
     * @return description
     */
    private static String printRow(Object[] objs, int horiCellPadding, int maxStringLen,
                                   Format format) {

        StringBuilder strBuilder = new StringBuilder();

        for (int i = 0; i < objs.length - 1; i++) {

            String objStr = (format == null) ? objs[i].toString() : format.format(objs[i]);

            strBuilder.append(objStr).append(StringUtil.getPadding(horiCellPadding
                    + (maxStringLen - objStr.length())));
        }

        String lastStr = (format == null)
                         ? objs[objs.length - 1].toString() : format.format(objs[objs.length - 1]);

        strBuilder.append(lastStr).append(StringUtil.getPadding((maxStringLen - lastStr.length())));

        return strBuilder.toString();
    }

    /**
     * Method description
     *
     * @param objs2D description
     *
     * @return description
     */
    private static int getMaxStringLen(Object[][] objs2D) {

        int maxStrLen = 0;

        for (Object[] objRow : objs2D) {

            for (Object obj : objRow) {

                maxStrLen = Math.max(maxStrLen, obj.toString().length());
            }
        }

        return maxStrLen;
    }

    /**
     * Method description
     *
     * @param objs2D description
     * @param format description
     *
     * @return description
     */
    private static int getMaxStringLen(Object[][] objs2D, Format format) {

        int maxStrLen = 0;

        for (Object[] objRow : objs2D) {

            for (Object obj : objRow) {

                maxStrLen = Math.max(maxStrLen, format.format(obj).length());
            }
        }

        return maxStrLen;
    }

    public static String toString(double[][] a, int horiCellPadding, int vertCellPadding,
                                  Format format) {
        return toString(ArrayUtil.getDoubleArray(a), horiCellPadding, vertCellPadding, format);
    }

    /**
     * This will recursively find the file(or dir) with name as <code>fileKey</code>
     * and delete them.
     * @param parentDir
     * @param fileKey
     * @return
     */
    private static boolean recursiveDelete(File parentDir, String fileKey) {

        if (parentDir.isFile()) return false;

        File[] subs         = parentDir.listFiles();
        boolean deletedSome = false;

        for (File file : subs) {

            if (file.getName().equals(fileKey)) {

                if (!delete(file)) {
                    System.out.println("could not delete file: " + file.getPath());
                } else {

                    System.out.println("deleted file: " + file.getPath());

                    deletedSome = true;
                }

            } else {
                if (file.isDirectory()) recursiveDelete(file, fileKey);
            }
        }

        return deletedSome;
    }

    /**
     * if this is a non-empty directory then deletes the entire directory
     * or if just a file then deletes it normally.
     * @param file
     * @return
     */
    private static boolean delete(File file) {

        if (file.isFile()) {
            return file.delete();
        }

        File[] subs = file.listFiles();

        for (File subFile : subs) {

            if (!delete(subFile)) {

                System.out.println("could not delete file: " + subFile.getPath());

                return false;
            }
        }

        return file.delete();
    }
}
